<template>
  <a-upload
    v-model:file-list="fileList"
    list-type="picture-card"
    class="single-img-upload"
    :show-upload-list="false"
    :before-upload="beforeUpload"
    :custom-request="customUpload"
    @change="handleChange"
  >
    <img
      class="single-img-upload__img-res"
      v-if="imageUrl"
      :src="imageUrl"
      alt="avatar"
    />
    <div v-else>
      <plus-outlined></plus-outlined>
      <div class="ant-upload-text">点击上传</div>
    </div>
    <div class="single-img-upload__loading-wrapper" style="position: absolute">
      <a-spin size="small" v-if="loading" />
    </div>
  </a-upload>
</template>

<script setup lang="ts">
import * as qiniu from 'qiniu-js'

import { fetchQiNiuToken } from '@/api/file/qiniu'
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import { ref } from 'vue'

interface FileItem {
  lastModified: number
  lastModifiedDate: Date
  uid: string
  name: string
  status: 'uploading' | 'done' | 'error' | 'removed'
  size: number
  originFileObj: File
  type: string
  response?: { imgUrl: string }
  xhr?: any
}

interface FileInfo {
  /** 进度 */
  event?: number
  file: FileItem
  fileList: FileItem[]
}

withDefaults(defineProps<{ imageUrl?: string }>(), {
  imageUrl: '',
})

const emits = defineEmits<{
  (e: 'update:imageUrl', imgUrl: string): void
}>()

// TODO 抽取重复逻辑方便后期做出不同的上传组件 目前想到的两种模式 多文件和单文件//多类型文件的上传
const fileList = ref<FileItem[]>([])
const loading = ref<boolean>(false)

const customUpload = async (options: {
  file: File
  onSuccess: any
  onProgress: any
  onError: any
}) => {
  const { onSuccess, onError, file, onProgress } = options
  // 获取七牛token
  const { data: token } = await fetchQiNiuToken({
    key: file.name,
    bucket: 'extra',
  })
  // 构建上传前配置
  const observable = qiniu.upload(
    file,
    file.name,
    token,
    { fname: file.name },
    { useCdnDomain: true }
  )
  // 上传开始
  const subscription = observable.subscribe({
    next: (res) => {
      onProgress({ percent: Math.floor(res.total.percent) })
    },
    error: (error) => {
      onError(error)
    },
    complete: (res) => {
      const response = { imgUrl: '//test.com/' + res.key }
      onSuccess(response)
    },
  })
}

const handleChange = (infos: unknown) => {
  const info = infos as FileInfo
  if (info.file.status === 'uploading') {
    console.log('info.file.status uploading', info)
    loading.value = true
    return
  }
  if (info.file.status === 'done') {
    console.log('info.file.status', info)
    if (info.file.response) {
      emits('update:imageUrl', info.file.response.imgUrl)
    }
    loading.value = false
  }
  if (info.file.status === 'error') {
    loading.value = false
    message.error('upload error')
  }
}

const beforeUpload = (file: FileItem) => {
  const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
  if (!isJpgOrPng) {
    message.error('只允许上传JPG 和 PNG文件')
  }
  return isJpgOrPng
}
</script>

<style lang="scss" scoped>
@include b(single-img-upload) {
  height: 100%;
  display: block;
  position: relative;
  @include e(img-res) {
    height: 100%;
    object-fit: cover;
  }
  @include e(loading-wrapper) {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
  }
}
</style>

<style lang="scss">
@include b(single-img-upload) {
  .ant-upload.ant-upload-select-picture-card {
    display: block;
    float: none;
    height: 100%;
    width: 100%;
    & > .ant-upload {
      display: flex;
      align-items: center;
      justify-content: center;
    }
  }
  .ant-spin-spinning {
    @include center-translate;
  }
}
</style>
